// CSE571 - game-manager.cpp
// Authors: Jonathan Wald, Daniel Sarfati, Guangle Fan
// This class manages all the different connections that a client-side player
// may have.  

#include <iostream>
#include "stdio.h"
#include "ace/Tokenizer_T.h"
#include "ace/Synch.h"

#include "game-manager.h"
#include "player-game.h"
#include "globals.h"
#include "signal-handler.h"

const static int DEALER_DEFAULT_PORT = 8000;
char buffer[MTU];

GameManager* GameManager::instance_ = NULL;

struct connect_to_game
  : std::unary_function<PlayerGame*, void> {

  connect_to_game(GameManager* manager) {
    manager_ = manager;
  }

  void operator() (PlayerGame* game) {
    ACE_INET_Addr peer_addr;
    if (peer_addr.set(game->get_port(), game->get_host().c_str()) == -1) {
      std::cout << "Unable to set server address for " <<
         game->get_host() << ":" << game->get_port() << std::endl;
      game->failed = true;
      return;
    }

    peer_addr.addr_to_string (buffer, MTU);

    PlayerSvcHandler* connector_handler = &(game->handler);
    if (manager_->connector->connect(connector_handler,
						    peer_addr) == -1) {
      std::cout << "Couldn't connect to server.\n" << std::endl;
      game->failed = true;
      GameManager::instance()->game_quit(game);
      return;
    }
    std::cout << "Connected to game " << game->game_name <<
      " on address and port: " << buffer << std::endl;
  }

  GameManager* manager_;
};

GameManager* GameManager::instance() {
   if (instance_ == NULL) {
      instance_ = new GameManager();
      instance_->num_of_scores_sended = 0;
      instance_->auto_act = false;
   }
   return instance_;
}

GameManager::GameManager() : plays_holdem(true),
			     plays_stud(true),
			     plays_draw(true) {} 

int GameManager::parse_file(const char* filename) {
   // Intialize the default host_name and port
   const int HOST_NAME_SIZE = 64;
   char host_name [HOST_NAME_SIZE];
   ::gethostname (host_name, HOST_NAME_SIZE - 1);
   short port = DEALER_DEFAULT_PORT;
   string game_name, game_type;
    
   // Attempt to open defintions file for reading
   ifstream def_file;
   def_file.open(filename);
    
   if (!def_file.is_open()) { return -1; }
  
   const int GAME_FLAG = 0, PORT_FLAG = 1, HOST_FLAG = 2;
   int flag;
  
   string line;
  
   // Iterate through the definitions file one line at a time
   while (def_file.good()) {
     vector<pair<string, string> > game_pair;
     
      getline(def_file, line);
      if (line.size() == 0) continue;
      char connection_line[line.size()];
      strcpy(connection_line, line.c_str());
    
      ACE_Tokenizer tokens(connection_line);
      tokens.delimiter_replace(' ',0);        // tokenize 'connection_line' delimited by spaces
        
      bool parse_error = false;
      string error_msg;
      
      // Iterate through the tokens two at a time. The first should be the flag
      // and the second should be the parameter. Possible errors include unknown
      // flags and missing parameters. If an error occurs, an error message is 
      // stored and the loop is broken out of, but the parsing will continue for
      // the next line.
      for(char* tok = tokens.next(); tok; tok = tokens.next()) {
         if (!strcmp(tok,"-g")) flag = GAME_FLAG;
         else if(!strcmp(tok,"-p")) flag = PORT_FLAG;
         else if(!strcmp(tok,"-i")) flag = HOST_FLAG;
         else {
            parse_error = true;
            error_msg = "Unknown flag.";
            break;
         }
        
         tok = tokens.next();    // this should be the parameter for the flag
        
         if ( (tok[0] == '-') || (strlen(tok) == 0) ) {
            parse_error = true;
            error_msg = "Missing parameter for flag.";
            break;
         }
         
         switch(flag) {
            case GAME_FLAG:
               game_type = tok;
               tok = tokens.next();
	       if ( (tok[0] == '-') || (strlen(tok) == 0) ) {
		 parse_error = true;
		 error_msg = "Game flag takes two parameters, name and type.";
		 break;
	       }
               game_name = tok;
	    
	       game_pair.push_back ( pair <string, string>(game_type, game_name) );	       
               break;
            case PORT_FLAG:
               port = atoi(tok);
               break;
            case HOST_FLAG:
               strcpy(host_name, tok);
               break;
            default:
            	parse_error = true;
            	error_msg = "Missing flag.";
         }
      }
      
      if (game_name.size() == 0 || game_type.size() == 0) {
         parse_error = true;
         error_msg = "A connection line must define at least a game name and/or game type.";
      }
      if (!(game_type == "draw" || game_type == "stud" || game_type == "holdem")) {
        parse_error = true;
        error_msg = "Game type must be either 'draw' or 'stud' or 'holdem'";
      }
      if (game_type == "draw" && !plays_draw) {
        parse_error = true;
        error_msg = "Draw game not currently supported.";
      } else if (game_type == "stud" && !plays_stud) {
        parse_error = true;
        error_msg = "Stud game not currently supported.";
      } else if (game_type == "holdem" && !plays_holdem) {
        parse_error = true;
        error_msg = "Holdem game not currently supported.";
      }

      if (parse_error) {
         std::cout << "PARSE ERROR IN [" << line << "]:\n - " << error_msg << std::endl;
         std::cout << "Attempting to parse remaining lines." << std::endl;
      }
      else {
	for (unsigned int i = 0; i < game_pair.size(); ++i) {
	  PlayerGame* game = new PlayerGame();
	  games.push_back(game);
	  game->port = port;
	  game->host = host_name;
	  game->handler.game = game;
	  game->game_type = game_pair[i].first;
	  game->game_name = game_pair[i].second;
          game->label = game->game_name;
	}
      }
      ::gethostname (host_name, HOST_NAME_SIZE - 1);
      port = DEALER_DEFAULT_PORT;
   }
  
   return 0;
}

void GameManager::game_quit (PlayerGame * game) {
  PlayerGame * comp;
  vector<PlayerGame *>::iterator gtemp = games.end(), giter;
  ACE_Guard<ACE_Thread_Mutex> guard(games_lock);
  for (giter = games.begin(); giter != games.end(); ++giter) {
    // here need to assign a new pointer for comparition since "*giter == game" will cause infinite recursion, and compiler keep self save by ignoring it.
    comp = *giter;
    if (comp == game) gtemp = giter;
  }
  if (gtemp != games.end()) games.erase (gtemp);
  // If -q flag is set and this was the last game, quit.
  if (games.size() == 0 && auto_act) {
    SigHandler::set_sigint();
  }
}

GameManager::~GameManager() {
   for (unsigned int i = 0; i < games.size(); ++i) {
      delete games[i];
   }
}

void GameManager::connect_all() {
   for_each(games.begin(), games.end(),
   	    connect_to_game(this));
}

// Service methods
int GameManager::init (int argc, char *argv[]) {
  if(argc != 1) {
    std::cout << "Expecting 1 filename for client (re)initialization." << std::endl;
    return -1;
  }
  parse_file(argv[0]);
  connect_all();
  return 0;
}

int GameManager::fini (void) {
  return 0;
}

int GameManager::suspend (void) {
  for (unsigned int i = 0; i < games.size(); ++i) {
    games[i]->suspend();
  }
  return 0;
}

int GameManager::resume (void) {
  for (unsigned int i = 0; i < games.size(); ++i) {
    games[i]->resume();
  }
  return 0;
}

int GameManager::info (char **, size_t) {
  std::cout << "Games supported by client service: "
	    << (plays_stud ? "Stud " : "")
	    << (plays_draw ? "Draw " : "")
	    << (plays_holdem ? "Holdem " : "")
	    << std::endl;


  std::cout << "Current info for client's game services: "
	    << std::endl;
  for (unsigned int i = 0; i < games.size(); ++i) {
    games[i]->info(NULL);
  }
  return 0;
}
